/*
 * Element.cpp
 *
 *  Created on: 02/mar/2013
 *      Author: lordmzn
 */

#include "MOLEM/Element.h"
#include <cmath>

// very weird behaviour: doesnt recognize NULL
#ifndef NULL
#define NULL 0
#endif

namespace MOLEM {

Element::Element(unsigned int xx, unsigned int yy) :
		z(ELEMENT_Z_DEFAULT_VALUE), x(xx), y(yy), west_el(NULL), north_el(NULL), east_el(
				NULL), south_el(NULL), boundary(Direction::NONE) {
}

Element::Element(unsigned int xx, unsigned int yy, Direction::DirEnum bord) :
		z(ELEMENT_Z_DEFAULT_VALUE), x(xx), y(yy), west_el(NULL), north_el(NULL), east_el(
				NULL), south_el(NULL), boundary(bord) {
}

Element::Element(const Element& e) :
		z(e.z), x(e.x), y(e.y), west_el(e.west_el), north_el(e.north_el), east_el(
				e.east_el), south_el(e.south_el), boundary(e.boundary) {
}

Element::~Element() {
}

bool Element::operator==(const Element& a) const {
	// check self
	if (this == &a) {
		return true;
	}
	// check fields
	if (z != a.z || x != a.x || y != a.y) {
		return false;
	}
	if (west_el != a.west_el) {
		return false;
	}
	if (north_el != a.north_el) {
		return false;
	}
	if (east_el != a.east_el) {
		return false;
	}
	if (south_el != a.south_el) {
		return false;
	}
	if (boundary != a.boundary) {
		return false;
	}
	return true;
}

bool Element::operator!=(const Element& a) const {
	return !(*this == a);
}

void Element::setZvalue(double val) {
	z = val;
}

double Element::getZvalue() const {
	return z;
}

double Element::getCartesianDistance(const Element& e) const {
	int deltax = x - e.x;
	int deltay = y - e.y;
	return sqrt(deltax * deltax + deltay * deltay);
}

bool Element::connect(Element& west, Element& north, Element& east,
		Element& south) {
	if (boundary != Direction::NONE) {
		switch (boundary) {
		case Direction::WEST:
			west_el = this;
			north_el = &north;
			east_el = &east;
			south_el = &south;
			return true;
		case Direction::NORTH_WEST:
			west_el = this;
			north_el = this;
			east_el = &east;
			south_el = &south;
			return true;
		case Direction::NORTH:
			west_el = &west;
			north_el = this;
			east_el = &east;
			south_el = &south;
			return true;
		case Direction::NORTH_EAST:
			west_el = &west;
			north_el = this;
			east_el = this;
			south_el = &south;
			return true;
		case Direction::EAST:
			west_el = &west;
			north_el = &north;
			east_el = this;
			south_el = &south;
			return true;
		case Direction::SOUTH_EAST:
			west_el = &west;
			north_el = &north;
			east_el = this;
			south_el = this;
			return true;
		case Direction::SOUTH:
			west_el = &west;
			north_el = &north;
			east_el = &east;
			south_el = this;
			return true;
		case Direction::SOUTH_WEST:
			west_el = this;
			north_el = &north;
			east_el = &east;
			south_el = this;
			return true;
		}
	}
	west_el = &west;
	north_el = &north;
	east_el = &east;
	south_el = &south;
	return true;
}

bool Element::isConnected() const {
	return west_el != NULL && north_el != NULL && east_el != NULL && south_el != NULL;
}

Element& Element::getNeighbour(Direction::DirEnum dir) {
	Element* neighbour = this;
	if (isConnected()) { // TODO remove the general condition and transform in a partial condition foreach direction
		Element* temp;
		switch (dir) {
		case (Direction::NORTH): {
			return *north_el;
		}
		case (Direction::NORTH_EAST): {
			temp = north_el;
			if (*temp != *this) {
				neighbour = temp->east_el;
				if (*temp != *neighbour) {
					return *neighbour;
				}
			}
			break;
		}
		case (Direction::EAST): {
			return *east_el;
		}
		case (Direction::SOUTH_EAST): {
			temp = south_el;
			if (*temp != *this) {
				neighbour = temp->east_el;
				if (*temp != *neighbour) {
					return *neighbour;
				}
			}
			break;
		}
		case (Direction::SOUTH): {
			return *south_el;
		}
		case (Direction::SOUTH_WEST): {
			temp = south_el;
			if (*temp != *this) {
				neighbour = temp->west_el;
				if (*temp != *neighbour) {
					return *neighbour;
				}
			}
			break;
		}
		case (Direction::WEST): {
			return *west_el;
		}
		case (Direction::NORTH_WEST): {
			temp = north_el;
			if (*temp != *this) {
				neighbour = temp->west_el;
				if (*temp != *neighbour) {
					return *neighbour;
				}
			}
			break;
		}
		case (Direction::NONE): {
			return *this;
		}
		}
	}
	return *neighbour;
}

} /* namespace MOLEM */
